package Servises;

import java.io.File;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import model.CustomPoint;
import model.Element;
import model.Movie;
import model.MovieEvent;
import model.Result;
import model.UrlFile;
import model.EventProperties.ClickEventProperties;
import model.EventProperties.MotionEventProperties;
import model.EventProperties.MoveablePictureEventProperties;
import model.EventProperties.PushMsgEventProperties;
import model.EventProperties.ReadFladEventProperties;
import model.Interface.IeventProperties;
import model.Interface.IresultProperties;
import model.ResultProperties.ChangeGlobleElementTextResultProprties;
import model.ResultProperties.KillEventResultProperties;
import model.ResultProperties.MoveToMovieResultProperties;
import model.ResultProperties.PlaySoundProperties;
import model.ResultProperties.SetFlagResultProperties;
import model.ResultProperties.ShowPictureResultProperties;
import model.ResultProperties.StartActivityProperties;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import Enums.EventType;
import Enums.FlagOptions;
import Enums.MotionType;
import Enums.ResultAction;
import Enums.Shape;
import Enums.UiElement;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;

public class SaxXmlParser {

	private static Document doc;

	private SaxXmlParser() {
		// single tone
	}
	
	public static boolean initParser(String fileName)
	{
		try {
			getListOfElements(fileName);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	public static Dictionary<String, Boolean> parseFlags(){
		Dictionary<String, Boolean> flags = null;
		flags = new Hashtable<String,Boolean>();
		try {
			NodeList listOfElements = doc.getElementsByTagName("Flag");

			for (int s = 0; s < listOfElements.getLength(); s++) {
				flags.put(listOfElements.item(s)
						.getAttributes().getNamedItem("name").getNodeValue(),
						Boolean.parseBoolean(listOfElements.item(s)
						.getAttributes().getNamedItem("initValue").getNodeValue()));

			}
		} catch (Throwable t) {
			t.printStackTrace();
		}
		return flags;
	} 
	
	public static Dictionary<Integer, Movie> parseMovies() {
		Dictionary<Integer, Movie> moviesResult = null;
		try {
			moviesResult = new Hashtable<Integer, Movie>();
			
			// init screen size
			CustomPoint.SizeF screenSize = new CustomPoint.SizeF();
			screenSize.height = Float.parseFloat(doc.getDocumentElement()
					.getAttributeNode("screenHeight").getNodeValue());
			screenSize.width = Float.parseFloat(doc.getDocumentElement()
					.getAttributeNode("screenWidth").getNodeValue());
			CustomPoint.setEditorScreenSize(screenSize);

			NodeList listOfMovies = doc.getElementsByTagName("Movie");

			for (int s = 0; s < listOfMovies.getLength(); s++) {

				Node movieNode = listOfMovies.item(s);
				NodeList listOfEvents = movieNode.getChildNodes().item(1)
						.getChildNodes();

				Map<Integer,MovieEvent> events = new TreeMap<Integer,MovieEvent>();

				for (int i = 1; i < listOfEvents.getLength(); i++) {

					int id = Integer.parseInt(listOfEvents.item(i)
							.getAttributes().getNamedItem("id").getNodeValue());
					EventType type = EventType.valueOf(listOfEvents.item(i)
							.getAttributes().getNamedItem("type")
							.getNodeValue());

					IeventProperties prop = null;

					switch (type) {
					case click: 
						prop = getClickEventPeoprties(listOfEvents.item(i));
						break;
						
					case voice: 
						break;
						
					case pushMsg:
						prop = getPushMsgProperties(listOfEvents.item(i));
						break;
					
					case motion: 
						prop = getMotionProperties(listOfEvents.item(i));
						break;
					
					case tuochScreen: 
						prop = getClickEventPeoprties(listOfEvents.item(i));
						break;
					
					case keybord: 
						break;
						
					case showMovablePicture:
						prop = getMovablePictureProperties(listOfEvents.item(i));
						break;
						
					case readFlag:
						prop = getReadFlagProperties(listOfEvents.item(i));
						break;
					}

					Node timeNode = listOfEvents.item(i).getChildNodes()
							.item(1);
					long startTime = timeNode.getChildNodes().item(1)
							.getNodeName().equalsIgnoreCase("Start") ? Long
							.parseLong(timeNode.getChildNodes().item(1)
									.getFirstChild().getNodeValue().trim()) : 0;
					long endTime = timeNode.getChildNodes().item(1)
							.getNextSibling().getNodeName().equalsIgnoreCase(
									"End") ? Long.parseLong(timeNode
							.getChildNodes().item(1).getNextSibling()
							.getFirstChild().getNodeValue().trim())
							: Long.MAX_VALUE;

					NodeList resultsYesNodes = timeNode.getNextSibling()
							.getChildNodes();
					Result[] resultsYes = new Result[0];
					if (resultsYesNodes.getLength() > 0)
					{
						resultsYes = new Result[resultsYesNodes
						                                 .getLength() - 1];
						for (int j = 1; j < resultsYesNodes.getLength(); j++) {
							resultsYes[j - 1] = getResult(resultsYesNodes.item(j));
						}
					}
					NodeList resultsNoNodes = timeNode.getNextSibling()
					.getNextSibling().getChildNodes();
					
					Result[] resultsNo = new Result[0];
					if (resultsNoNodes.getLength() > 0)
					{
						resultsNo = new Result[resultsNoNodes.getLength() - 1];
						for (int j = 1; j < resultsNoNodes.getLength(); j++) {
							resultsNo[j - 1] = getResult(resultsNoNodes.item(j));
						}
					}

					events.put(id,new MovieEvent(id, startTime, endTime, type,
							prop, resultsYes, resultsNo));

				}
				Integer movieId = Integer.parseInt(movieNode.getAttributes()
						.getNamedItem("id").getNodeValue());
				UrlFile urlFile = getUrlFile(movieNode);
				Boolean isOnline = Boolean.parseBoolean(movieNode
						.getAttributes().getNamedItem("isOnline")
						.getNodeValue());

				NodeList resultsEndNodes = movieNode.getLastChild()
						.getChildNodes();
				
				Result[] resultsEnd = new Result[0];
				if (resultsEndNodes.getLength() > 0)
				{
					resultsEnd = new Result[resultsEndNodes.getLength() - 1];
					for (int j = 1; j < resultsEndNodes.getLength(); j++) {
						resultsEnd[j - 1] = getResult(resultsEndNodes.item(j));
					}
				}

				moviesResult.put(movieId, new Movie(movieId, events, urlFile,
						isOnline, resultsEnd));
			}

		} catch (Throwable t) {
			t.printStackTrace();
		}

		return moviesResult;

	}

	private static IeventProperties getReadFlagProperties(Node item) {
		String flagName = item.getAttributes().getNamedItem("name").getNodeValue();
		
		return new ReadFladEventProperties(flagName);
	}
	
	private static IeventProperties getPushMsgProperties(Node item) {
		String msgName = item.getAttributes().getNamedItem("name").getNodeValue();
		
		return new PushMsgEventProperties(msgName);
	}
	

	private static IeventProperties getMovablePictureProperties(Node item) {
		UrlFile url = getUrlFile(item);
		Rect primeryLocation = getRectLocation(item,"primLocationTopLeft","primLocationBottomRight");
		Rect SecLocation = getRectLocation(item,"secLocationTopLeft","secLocationBottomRight");
		int numOfFrames = Integer.parseInt(item.getAttributes().getNamedItem("numOfFrames").getNodeValue());
		
		return new MoveablePictureEventProperties(primeryLocation,SecLocation,url,numOfFrames);
	}

	private static IeventProperties getMotionProperties(Node item) {
		MotionType type = MotionType.valueOf(item.getAttributes().getNamedItem("motionType").getNodeValue());
		return new MotionEventProperties(type);
	}

	public static Dictionary<Integer, Element> parseElements() {
		Dictionary<Integer, Element> elementsResult = null;
		elementsResult = new Hashtable<Integer, Element>();
		try {
			NodeList listOfElements = doc.getElementsByTagName("Element");

			for (int s = 0; s < listOfElements.getLength(); s++) {
				elementsResult.put(Integer.parseInt(listOfElements.item(s)
						.getAttributes().getNamedItem("id").getNodeValue()),
						getElement(listOfElements.item(s)));

			}
		} catch (Throwable t) {
			t.printStackTrace();
		}
		return elementsResult;
	}

	private static void getListOfElements(String fileName)
			throws FactoryConfigurationError, ParserConfigurationException,
			SAXException, IOException {
		
		DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
				.newInstance();
		DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
		doc = docBuilder.parse(new File(fileName));

		// normalize text representation
		doc.getDocumentElement().normalize();
	}
	
	public static void freeParser()
	{
		doc = null;
		System.gc();
	}
	
	public static int parseMovieStartId() {
		int movie = 1;
		try {
			
			NodeList listOfElements = doc.getElementsByTagName("StartingMovieId");

			movie = Integer.parseInt(listOfElements.item(0).getFirstChild().getNodeValue().toString());
			
		} catch (Throwable t) {
			t.printStackTrace();
		}
		return movie;
	}

	private static UrlFile getUrlFile(Node node) {
		String url = node.getAttributes().getNamedItem("url").getNodeValue();
		String movieFileName = node.getAttributes().getNamedItem("fileName")
				.getNodeValue();
		String fileSuffix = node.getAttributes().getNamedItem("fileSuffix")
				.getNodeValue();
		return new UrlFile(url, movieFileName, fileSuffix);
	}

	private static Result getResult(Node item) {
		ResultAction action = ResultAction.valueOf(item.getAttributes()
				.getNamedItem("type").getNodeValue());
		String checkFlag = null;
		if (item.getAttributes().getNamedItem("checkFlag") != null)
		{
			checkFlag = item.getAttributes().getNamedItem("checkFlag").getNodeValue();
		}
		
		IresultProperties prop;
		switch (action) {
		case startActivity:
		{
			String activityName = item.getAttributes().getNamedItem("name").getNodeValue();
			prop = new StartActivityProperties(activityName);
			break;
		}
		case killEvent:
		{
			int eventId = Integer.parseInt(item.getAttributes().getNamedItem("eventId").getNodeValue());
			prop = new KillEventResultProperties(eventId);
			break;
			
		}
		case setFlag:
		{
			String flagName = item.getAttributes().getNamedItem("name").getNodeValue();
			String flagValueString = item.getAttributes().getNamedItem("value").getNodeValue();
			FlagOptions flagValue = FlagOptions.valueOf(flagValueString);
			
			prop = new SetFlagResultProperties(flagName, flagValue);
			
			break;
		}
		case moveToMovie: {
			int id = Integer.parseInt(item.getAttributes().getNamedItem(
					"movieId").getNodeValue());
			int time = Integer.parseInt(item.getAttributes().getNamedItem(
					"time").getNodeValue());
			Node tmpNode = item.getAttributes().getNamedItem(
			"IsSmoothTransition");
			if (tmpNode == null)
			{
				prop = new MoveToMovieResultProperties(id, time, false);
				break;
			}
			String isSmoothStr = tmpNode.getNodeValue();
			if (isSmoothStr == null || isSmoothStr == "")
				isSmoothStr = "false";
			boolean smoothTrans = Boolean.parseBoolean(isSmoothStr);
			//For now we don't support smooth trans since it
			//drains all the memory
			smoothTrans = false;
			prop = new MoveToMovieResultProperties(id, time, smoothTrans);
			break;
		}
		case changeGlobalElementText: {
			int id = Integer.parseInt(item.getAttributes().getNamedItem(
					"elementId").getNodeValue());
			String text = item.getAttributes().getNamedItem("text")
					.getNodeValue();
			prop = new ChangeGlobleElementTextResultProprties(id, text);
			break;
		}

		case playAudio: {
			Boolean isMoviePaused = Boolean.parseBoolean(item.getAttributes()
					.getNamedItem("isMoviePaused").getNodeValue());
			prop = new PlaySoundProperties(getUrlFile(item), isMoviePaused);
			break;
		}
		case showPicture: {
			int duration = Integer.parseInt(item.getAttributes().getNamedItem(
					"duration").getNodeValue());

			Boolean isFlickering = Boolean.parseBoolean(item.getAttributes()
					.getNamedItem("isFlickering").getNodeValue());

			Boolean isMoviePaused = Boolean.parseBoolean(item.getAttributes()
					.getNamedItem("isMoviePaused").getNodeValue());

			Rect rect = getRectLocation(item);
			
			int numOfFrames = Integer.parseInt(item.getAttributes().
					getNamedItem("numOfFrames").getNodeValue());

			prop = new ShowPictureResultProperties(getUrlFile(item), duration,
					isFlickering, rect, isMoviePaused,numOfFrames);
			break;
		}

		default:
			prop = null;
			break;
		}
		return new Result(action, prop,checkFlag);
	}

	private static Rect getRectLocation(Node item) {
		return getRectLocation(item, "locationTopLeft", "locationBottomRight");
	}
	
	private static Rect getRectLocation(Node item,String topLeftAttName, String bottemAttRightName) {
		String locationTopLeft = item.getAttributes().getNamedItem(
				topLeftAttName).getNodeValue();
		String locationBottomRight = item.getAttributes().getNamedItem(
				bottemAttRightName).getNodeValue();
		CustomPoint[] rect = new CustomPoint[2];
		rect[0] = getScreenLocation(locationTopLeft);
		rect[1] = getScreenLocation(locationBottomRight);

		return new Rect((int) Math.floor(rect[0].getPoint().x), (int) Math
				.floor(rect[0].getPoint().y), (int) Math.floor(rect[1]
				.getPoint().x), (int) Math.floor(rect[1].getPoint().y));
	}

	private static CustomPoint getScreenLocation(String location) {
		return new CustomPoint(new PointF(Float.parseFloat(location.substring(
				0, location.indexOf(","))), Float.parseFloat(location
				.substring(location.indexOf(",") + 1, location.length()))));
	}

	private static IeventProperties getClickEventPeoprties(Node propNode) {
		
		Boolean isVisible = Boolean.parseBoolean(propNode.getAttributes()
				.getNamedItem("isVisible").getNodeValue());
		Boolean isShowClickEffect = Boolean.parseBoolean(propNode
				.getAttributes().getNamedItem("isShowClickEffect")
				.getNodeValue());
		String colorSt = propNode.getAttributes().getNamedItem(
				"clickEffectColor").getNodeValue().trim();
		int clickEffectColor = getColor(colorSt);
		UrlFile url = getUrlFile(propNode);

		return new ClickEventProperties(getElement(propNode), isVisible,
				isShowClickEffect, clickEffectColor, url);

	}

	private static Element getElement(Node propNode) {
		String colorSt;
		Rect rect = getRectLocation(propNode);
		UiElement uiElement = UiElement.valueOf(propNode.getAttributes()
				.getNamedItem("elementType").getNodeValue());
		colorSt = propNode.getAttributes().getNamedItem("color").getNodeValue()
				.trim();
		int color = getColor(colorSt);
		String text = propNode.getAttributes().getNamedItem("text")
				.getNodeValue();
		// Shape shape =
		// Shape.valueOf(propNode.getAttributes().getNamedItem("shape").getNodeValue());
		Shape shape = Shape.rectangle;
		Boolean isFlickering = Boolean.parseBoolean(propNode.getAttributes()
				.getNamedItem("isFlickering").getNodeValue());

		return new Element(rect, uiElement, color, text, shape, isFlickering);
	}

	private static int getColor(String color) {

		int a = Integer.parseInt(color.substring(color.lastIndexOf(",") + 1,
				color.length()));
		int r = Integer.parseInt(color.substring(0, color.indexOf(",")));
		color = color.substring(color.indexOf(",") + 1);
		int g = Integer.parseInt(color.substring(0, color.indexOf(",")));
		color = color.substring(color.indexOf(",") + 1);
		int b = Integer.parseInt(color.substring(0, color.indexOf(",")));

		return Color.argb(a, r, g, b);
	}

}
